import asyncio
import io
from typing import List, Union

from fastapi import File, UploadFile, APIRouter, Query

from config import config_minio
from exception.exception import CustomException
from model.schemas import Response, Success, Fail, UpdateFileDto, FileVo, FilePage
from service import service_file
from service.service_minio import put_object
from tools import uuid_tool
import threading

router = APIRouter(tags=["文件接口"])

updateLock = threading.Lock()

deleteLock = threading.Lock()


@router.get("/file", response_model=Response, description="文件列表", summary="文件列表")
def list_file(current: int = 0,
                    size: int = 4,
                    condition: Union[str, None] = Query(default=None, title="查询条件"),
                    query: Union[str, None] = Query(default=None, title="查询值")):
    """
    获取文件列表
    @return: 文件列表
    """
    try:
        files: List[FileVo] = service_file.list_file(current, size, condition, query)
        total: int = service_file.count_file(condition, query)
        file_list = FilePage(files=files, total=total, current=current, size=size)

        return Success(data=file_list)
    except CustomException as e:
        return Fail(code=e.status_code, message=e.detail, data=e.data)


@router.post("/file", response_model=Response, description="上传文件", summary="上传文件")
async def upload_file(file: UploadFile = File(...)):
    """ 上传文件
        1、后端接收到前端的 UploadFile
        2、bucket ：全部设置为 bucket-ys-2
        3、生成一个 uuid 用于标识文件（上传时使用 uuid+存储桶）
        4、获取文件大小 size，file.size() 直接获取，记录到数据库
        5、文件名：从 fileupload 对象可以直接获取
        6、文件类型：文件名.spilt(".")[1]
        7、上传文件到 minio
        8、url，上传 minio 之后，获取签名，不设置过期时间
    """
    try:
        bucket: str = config_minio.BUCKET_NAME
        name: str = file.filename
        file_type: str = name.split(".")[-1]
        uuid: str = uuid_tool.generate_uuid() + "." + file_type
        size = file.size
        binary_file = io.BytesIO(await file.read())
        # 重置文件指针到开头以便后续读取
        binary_file.seek(0)
        # 上传 minio ，然后获取 url
        put_object(bucket_name=bucket,
                   object_name=uuid,
                   data=binary_file,
                   length=file.size,
                   part_size=-1)
    except Exception as e:
        return Fail(code=500, message="上传 minio 失败", data=str(e))
    try:
        file = service_file.upload_file(bucket, uuid, size, name, file_type)
        return Success(data=file.name)
    except CustomException as e:
        return Fail(code=e.status_code, message=e.detail, data=e.data)


@router.delete("/file", response_model=Response, description="删除文件", summary="删除文件")
def delete_file(uuid: str, name: str):
    """
    删除文件
    @param uuid: uuid，包括删除 minio 的文件和 mysql 中的记录
    @param name: 真实文件名
    @return:
    """
    with deleteLock:
        try:
            bucket = config_minio.BUCKET_NAME
            res = service_file.delete_file(uuid, name, bucket)
            return Success(data=res)
        except CustomException as e:
            return Fail(code=e.status_code, message=e.detail, data=e.data)


@router.put("/file", response_model=Response, description="修改文件名", summary="修改文件名")
def update_file(update: UpdateFileDto):
    """
    修改文件名
    @param update: 修改的请求体参数，包括文件 id 和新文件名
    @return:
    """
    with updateLock:
        try:
            uuid = update.uuid
            new_name = update.new_name
            old_name = update.old_name

            res = service_file.update_file(uuid, new_name, old_name)
            return Success(data=res)
        except CustomException as e:
            return Fail(code=e.status_code, message=e.detail, data=e.data)
